const express = require("express")
const multer = require("multer");
const jpeg = require('jpeg-js')
const tf = require('@tensorflow/tfjs')
const nsfw = require('nsfwjs')
const sharp = require('sharp')
const cors = require('cors')
const fs = require('fs');
const https = require('https');

// 启用生产模式
// tf.enableProdMode();
// tf.enableDebugMode()
const app = express()
const upload = multer();


// 允许跨域
// 使用 cors 中间件，配置允许跨域访问
app.use(cors({
    // origin: 'http://localhost:8000', // 或者 '*' 来允许所有来源， localDev模式
    origin: 'https://www.yhsimon.cn', // uat环境
    credentials: false, // 如果你的请求需要携带凭证（如 cookies），请设置为 true
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的方法
    allowedHeaders: ['Content-Type', 'Authorization'], // 允许的头部，包括 Content-Type
    preflightContinue: false, // 如果设置为 true，则在预检请求后不会停止请求
    optionsSuccessStatus: 204 // 预检请求的响应状态码
}));

// 读取SSL证书和私钥
const options = {
    key: fs.readFileSync('./cert/nsfwjs.yhsimon.cn.key'),
    cert: fs.readFileSync('./cert/nsfwjs.yhsimon.cn_bundle.crt'),
};


// 客户端使用
// curl --request POST localhost:8004/nsfw --form "image=@D:/Documents/VSCode/realm-vue/nsfwjs-server/public/avatar.png"

let _model
const convert = async (img) => {
    // Decoded image in UInt8 Byte array
    let buffer = await sharp(img).toFormat("jpeg").toBuffer()
    const image = await jpeg.decode(buffer)
    const numChannels = 3
    const numPixels = image.width * image.height
    const values = new Int32Array(numPixels * numChannels)
    for (let i = 0; i < numPixels; i++)
        for (let c = 0; c < numChannels; ++c)
            values[i * numChannels + c] = image.data[i * 4 + c]

    return tf.tensor3d(values, [image.height, image.width, numChannels], 'int32')
}

app.post("/nsfw", upload.single("image"), async (req, res) => {
    if (!req.file) res.status(400).send("Missing image multipart/form-data");
    else {
        const image = await convert(req.file.buffer);
        const predictions = await _model.classify(image, 1);
        image.dispose();
        res.json(predictions);
    }
});

const load_model = async () => {
    // _model = await nsfw.load("InceptionV3");
    // _model = await nsfw.load("MobileNetV2");
    // _model = await nsfw.load("InceptionV3");
    _model = await nsfw.load("MobileNetV2Mid");
};

const server = https.createServer(options, app);

// Keep the model in memory, make sure it's loaded only once
load_model().then(() => server.listen(8504));